<script lang="ts" setup>
import { ref, watch, type PropType } from 'vue';
import { useFormItem, Notification, type FileItem } from '@arco-design/web-vue';
import type { ListType } from '@arco-design/web-vue/es/upload/interfaces';
import prettyBytes from 'pretty-bytes';
import useUploader from '@/settings/hooks/uploader';
import { useI18n } from 'vue-i18n';

defineOptions({
  name: 'OpenUpload',
});

export interface OpenUploadFile extends File {
  __data: any;
}

const emit = defineEmits(['update:modelValue', 'success', 'error']);

const props = defineProps({
  limit: {
    type: Number,
    default: 1,
  },

  modelValue: {
    type: [String, Array],
    default: '',
  },

  accept: {
    type: String,
    default: 'image/*',
  },

  maxFileSize: {
    type: Number,
    default: 1000 * 1000 * 10,
  },

  listType: {
    type: String as PropType<ListType>,
    default: 'picture',
  },

  imageAspectRatio: {
    type: Number,
    default: NaN,
  },

  simple: {
    type: Boolean,
    default: false,
  },
});

const { t } = useI18n();
const { mergedDisabled, eventHandlers } = useFormItem();
const { getHashFileName, getUploadSignature } = useUploader();
const getUid = () => String(+new Date()) + String(Math.floor(Math.random() * 9999));
const fileList = ref<FileItem[]>([]);
const loading = ref(false);

const updateFileList = (value: string | unknown[] = props.modelValue) => {
  if (props.simple) {
    return;
  }

  if (props.limit === 1) {
    fileList.value = value ? [{
      ...fileList.value[0],
      uid: fileList.value[0]?.uid || getUid(),
      url: value as string,
    }] : [];
  }
  else {
    fileList.value = (value as []).map((item, index) => ({
      ...fileList.value[index],
      url: item,
    } as FileItem));
  }
}

updateFileList();

watch(() => props.modelValue, (value, oldValue) => {
  if (value === oldValue) {
    return;
  }

  updateFileList(value);
});

watch(fileList, (value, oldValue) => {
  if (props.simple) {
    return;
  }

  if (props.limit === 1) {
    const url = value[0]?.url;
    const oldUrl = oldValue[0]?.url;
    const status = value[0]?.status;

    if (status === 'uploading' && url === undefined) {
      // 非图片需要一些特殊处理，防止出现奇怪的表单未验证通过的提示
      // 这是因为 AUpload 组件内部的 eventHandlers.value?.onChange?.() 方法引起
      // 他会在上传过程中就引发表单验证，但在这个组件里不需要他处理，但是无法关掉，
      // 所以在这里使用一个中间状态，可以防止出现奇怪的表单未验证通过的提示
      emit('update:modelValue', value[0]?.name ?? '...');
      return;
    }

    if (url === oldUrl) {
      return;
    }

    emit('update:modelValue', url ?? '');

    eventHandlers.value?.onChange?.();
  }
  else {
    emit('update:modelValue', value.map((item) => item.url));
  }
});

const action = ref('');
const uploadData = ref({});

const onBeforeUpload = async (file: File) => {
  if (file.size > props.maxFileSize) {
    Notification.error({
      content: t('settings.notification.fileLarge', { size: prettyBytes(props.maxFileSize) }),
      duration: 10 * 1000,
    });

    return false;
  }

  const { isSuccess, data } = await getUploadSignature();

  if (!isSuccess) {
    return false;
  }

  const md5Filename = await getHashFileName(file);

  if (!md5Filename) {
    return false;
  }

  action.value = data.host;

  uploadData.value = {
    key: `${data.directory}${md5Filename}`,
    OSSAccessKeyId: data.accessKeyId,
    policy: data.policy,
    signature: data.signature,
    success_action_status: '200',
  };

  (file as OpenUploadFile).__data = {
    ...uploadData.value,
    urlPrefix: data.url,
  };

  loading.value = true;

  return file;
}

const getResponseUrlKey = (fileItem: FileItem) => {
  if (fileItem.file) {
    const fileData = (fileItem.file as OpenUploadFile).__data;
    return `${fileData.urlPrefix}/${fileData.key}`;
  }

  return '';
}

const onSuccess = (fileItem: FileItem) => {
  loading.value = false;

  emit('success', fileItem.url);
}

const onError = (fileItem: FileItem) => {
  loading.value = false;

  emit('error', fileItem);
}
</script>

<template>
  <AUpload
    class="open-upload"
    :class="{ 'open-upload-disabled': mergedDisabled }"
    v-model:file-list="fileList"
    :action="action"
    :data="uploadData"
    :limit="props.limit"
    :accept="props.accept"
    :response-url-key="getResponseUrlKey"
    :list-type="props.listType"
    :show-link="false"
    :disabled="mergedDisabled"
    :show-file-list="!props.simple"
    :show-upload-button="{ showOnExceedLimit: props.simple }"
    @before-upload="onBeforeUpload"
    @success="onSuccess"
    @error="onError"
  >
    <template #upload-button>
      <template v-if="$slots['upload-button']">
        <slot :loading="loading" name="upload-button"></slot>
      </template>
      <template v-else>
        <AButton>
          <template #icon>
            <IconUpload />
          </template>
          {{ $t('settings.component.upload.button') }}
        </AButton>
      </template>
    </template>

    <template #image="{ fileItem, index }">
      <AImage :src="fileItem.url" :key="index" show-loader />
    </template>

    <template #file-name="{ fileItem }">
      <ALink :href="fileItem.url" :hoverable="false" target="_blank">{{ decodeURIComponent(fileItem.url) }}</ALink>
    </template>

    <template #retry-icon>
    </template>
  </AUpload>
</template>

<style lang="scss" scoped>
.open-upload {
  &:deep() {
    .arco-upload-list.arco-upload-list-type-picture {
      .arco-upload-list-item {
        position: relative;
        display: block;
        margin-top: 10px;

        .arco-upload-list-item-content {
          display: block;
          padding: 0;
          background-color: transparent;
          border-radius: 0;

          .arco-upload-list-item-thumbnail {
            display: block;
            width: auto;
            height: auto;
            margin-right: 0;

            .arco-image:not(.arco-image-loading-error) {
              cursor: pointer;
            }

            img {
              display: block;
              // width: auto;
              max-width: 100%;
              height: auto;
              max-height: 150px;
            }
          }

          .arco-upload-list-item-name {
            display: none;
          }

          .arco-upload-progress {
            display: flex;
            align-items: center;
            height: 20px;
            margin-top: 5px;
            margin-left: 0;

            .arco-upload-icon.arco-upload-icon-success {
              cursor: default;
            }
          }
        }

        .arco-upload-list-item-operation {
          position: absolute;
          bottom: 3px;
          left: 24px;
          margin-left: 0;
        }
      }
    }
  }

  &.open-upload-disabled {
    &:deep() {
      .arco-upload-list-item-operation {
        display: none;
      }
    }
  }
}
</style>
